﻿using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using Autodesk.Windows;
using DonetArxLib;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using Application = Autodesk.AutoCAD.ApplicationServices.Application;

using Autodesk.AutoCAD.Interop;

[assembly: CommandClass(typeof(CommandEntry))]

namespace DonetArxLib
{
    public class CommandEntry
    {
        [CommandMethod("xsTest")]
        public void Test()
        {
        }

        [CommandMethod("JY_Export_text")]
        public void ExportText()
        {
            TextProcess tp = new TextProcess();
            tp.ExplodeMText = false;
            tp.GatherAllTextInfo();
            tp.SaveToFile(true);
            //tp.DrawAllTextExtern();
            UtilFunc.PrintMessage("done!");
        }

        /// <summary>
        /// 选择多段线，将其转换为曲线
        /// </summary>
        /// <exception cref="ArgumentNullException"></exception>
        [CommandMethod("xsPoly2dconvert")]
        public void Poly2DConvert()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            if (doc == null)
                throw new ArgumentNullException("no document!");

            TypedValue[] tv = new TypedValue[1];
            tv.SetValue(new TypedValue((int)DxfCode.Start, "POLYLINE"), 0);
            SelectionFilter sf = new SelectionFilter(tv);

            var ed = doc.Editor;
            var opt = new PromptSelectionOptions
            {
                RejectObjectsFromNonCurrentSpace = true,
                RejectObjectsOnLockedLayers = true,
                AllowSubSelections = false,
                SingleOnly = false
            };

            int tol = 3;

            opt.Keywords.Add("ZDWC", "ZDWC", "设置最大毫米误差(ZDWC)");
            opt.MessageForAdding = "请选择要处理的二维多段线 或" + opt.Keywords.GetDisplayString(true);
            opt.KeywordInput += (sender, e) =>
            {
                if (e.Input.ToUpper() == "ZDWC")
                {
                    PromptIntegerOptions dopt = new PromptIntegerOptions($"请输入允许的最大整数毫米误差<{tol}>");
                    dopt.LowerLimit = 1;
                    dopt.AllowNegative = false;
                    var res = ed.GetInteger(dopt);
                    if (res.Status == PromptStatus.OK)
                        tol = res.Value;
                }
            };

            var ssresult = ed.GetSelection(opt, sf);
            if (ssresult.Status != PromptStatus.OK)
                return;

            var ids = ssresult.Value.GetObjectIds();

            using (var trans = doc.TransactionManager.StartTransaction())
            {
                foreach (var id in ids)
                {
                    var pl = trans.GetObject(id, OpenMode.ForRead) as Polyline2d;
                    if (pl == null)
                        continue;

                    CuveProjection.CurveMarker marker = new CuveProjection.CurveMarker();
                    marker.Mark(pl, tol);
                }

                trans.Commit();
            }
        }

        #region Test Function

        private void TextMerge()
        {
            var textIds = UtilFunc.SelectEntities("TEXT", "please select text:");
            if (textIds == null || textIds.Count < 1)
                return;

            TextMergeUtil merge = new TextMergeUtil();
            var strings = merge.GetMergedTextString(textIds);
            foreach (var s in strings)
                UtilFunc.PrintMessage($"\n{s}");

            return;
        }

        private void CalcMatrix()
        {
            var mtx = Matrix3d.AlignCoordinateSystem(
                Point3d.Origin,
                Vector3d.XAxis,
                Vector3d.YAxis,
                Vector3d.ZAxis,
                new Point3d(1, 1, 0),
                Vector3d.XAxis,
                Vector3d.YAxis,
                Vector3d.ZAxis);

            var pt = new Point3d(1, 1, 0);

            var pt1 = pt.TransformBy(mtx);

        }

        private void CalcViewPort2()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;

            var id = UtilFunc.SelectEntity("VIEWPORT");
            if (id == ObjectId.Null)
                return;

            using (var trans = db.TransactionManager.StartTransaction())
            {
                var vp = UtilFunc.GetObject(trans, id, OpenMode.ForRead) as Viewport;
                if (vp == null)
                    return;

                var vCenter = vp.ViewCenter;
                var vTarget = vp.ViewTarget;

                var mtx = Matrix3d.AlignCoordinateSystem(
                    Point3d.Origin,
                    Vector3d.XAxis,
                    Vector3d.YAxis,
                    Vector3d.ZAxis,
                    vTarget,
                    vp.Ucs.Xaxis,
                    vp.Ucs.Yaxis,
                    vp.Ucs.Zaxis);

                var centerInModel = vCenter.ToPoint3d().TransformBy(mtx);

                var mtxScale = Matrix3d.Scaling(1 / vp.CustomScale, centerInModel);

                var mtxTmp = mtx.PreMultiplyBy(mtxScale);

                var centerPt = new Point3d(vTarget.X + vCenter.X, vTarget.Y + vCenter.Y, 0);
                UtilFunc.AddLineToModelSpace(Point3d.Origin, centerPt);

                var height = vp.Height / vp.CustomScale;
                var width = vp.Width / vp.CustomScale;

                var ptLT = new Point3d(centerPt.X - width / 2, centerPt.Y + height / 2, 0);
                var ptRB = new Point3d(centerPt.X + width / 2, centerPt.Y - height / 2, 0);
                UtilFunc.AddLineToModelSpace(ptLT, ptRB);

                var ptLB = new Point3d(centerPt.X - width / 2, centerPt.Y - height / 2, 0);
                var ptRT = new Point3d(centerPt.X + width / 2, centerPt.Y + height / 2, 0);
                UtilFunc.AddLineToModelSpace(ptLB, ptRT);

                trans.Commit();
            }

        }

        /// <summary>
        /// cad状态栏加小图标
        /// </summary>
        private void TestTrayItem()
        {
            var item = new Autodesk.AutoCAD.Windows.TrayItem();
            item.ToolTipText = "jyht";
            item.Visible = true;


            var index = Application.StatusBar.TrayItems.Add(item);
            for (int i = 0; i < Application.StatusBar.TrayItems.Count; i++)
            {
                item = Application.StatusBar.TrayItems[i];
                UtilFunc.PrintMessage(item.ToolTipText);
            }
        }


        /// <summary>
        /// calculate view port in modelspace
        /// </summary>
        private void CalcViewPort()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;

            var id = UtilFunc.SelectEntity();
            if (id == ObjectId.Null)
                return;

            using (var trans = db.TransactionManager.StartTransaction())
            {
                var vp = UtilFunc.GetObject(trans, id, OpenMode.ForRead) as Viewport;
                if (vp == null)
                    return;

                var vCenter = vp.ViewCenter;
                var vTarget = vp.ViewTarget;

                var centerPt = new Point3d(vTarget.X + vCenter.X, vTarget.Y + vCenter.Y, 0);
                UtilFunc.AddLineToModelSpace(Point3d.Origin, centerPt);

                var height = vp.Height / vp.CustomScale;
                var width = vp.Width / vp.CustomScale;

                var ptLT = new Point3d(centerPt.X - width / 2, centerPt.Y + height / 2, 0);
                var ptRB = new Point3d(centerPt.X + width / 2, centerPt.Y - height / 2, 0);
                UtilFunc.AddLineToModelSpace(ptLT, ptRB);

                var ptLB = new Point3d(centerPt.X - width / 2, centerPt.Y - height / 2, 0);
                var ptRT = new Point3d(centerPt.X + width / 2, centerPt.Y + height / 2, 0);
                UtilFunc.AddLineToModelSpace(ptLB, ptRT);

                trans.Commit();
            }
        }

        private void ReadDict()
        {
            const string filterDictName = "ACAD_FILTER";
            const string spatialName = "SPATIAL";

            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;

            PromptEntityOptions peo = new PromptEntityOptions("\nSelect a block reference to clip:");
            peo.AllowNone = false;
            peo.SetRejectMessage("\nNot a block reference.");
            peo.AddAllowedClass(typeof(BlockReference), false);
            PromptEntityResult per = ed.GetEntity(peo);
            if (per.Status != PromptStatus.OK)
                return;
            var blkRefId = per.ObjectId;


            using (var trans = db.TransactionManager.StartTransaction())
            {
                var br = UtilFunc.GetObject(trans, blkRefId, OpenMode.ForRead) as BlockReference;
                if (br == null) return;

                var extDic = UtilFunc.GetObject(trans, br.ExtensionDictionary, OpenMode.ForRead) as DBDictionary;
                if (extDic == null) return;

                if (extDic.Contains(filterDictName))
                {
                    DBDictionary fDict = UtilFunc.GetObject(trans, extDic.GetAt(filterDictName), OpenMode.ForWrite) as DBDictionary;
                    if (fDict.Contains(spatialName))
                    {
                        var id = fDict.GetAt(spatialName);
                        //SpatialFilter sf = DotnetUtil.GetObject(trans, id, OpenMode.ForRead) as SpatialFilter;
                        //if (sf == null)
                        //    Debug.Assert(false);

                        Xrecord xr = UtilFunc.GetObject(trans, id, OpenMode.ForRead) as Xrecord;
                        ResultBuffer rb = xr.Data;

                        int i = 0;
                        foreach (var item in rb)
                        {
                            ed.WriteMessage($"\n{i}: {item.Value} + ");
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 模拟xclip命令，可以实现对BlockReference的遮挡效果（对外部参照也适用）
        /// </summary>
        public void SpitialClipTest()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            var ed = doc.Editor;

            PromptEntityOptions peo = new PromptEntityOptions("\nSelect a block reference to clip:");
            peo.AllowNone = false;
            peo.SetRejectMessage("\nNot a block reference.");
            peo.AddAllowedClass(typeof(BlockReference), false);
            PromptEntityResult per = ed.GetEntity(peo);
            if (per.Status != PromptStatus.OK)
                return;
            var blkRefId = per.ObjectId;

            // 指定xclip位置 在wcs坐标系
            var pt1 = Point3d.Origin;
            var pt2 = new Point3d(59400, 42000, 0);

            UtilFunc.XClipBlockReference(db, pt1, pt2, blkRefId);
        }

        public void ModifyBlockAttr()
        {
            var id = UtilFunc.SelectEntity();
            if (id == ObjectId.Null)
                return;

            var db = Application.DocumentManager.MdiActiveDocument.Database;

            using (var trans = db.TransactionManager.StartTransaction())
            {
                var blkRef = UtilFunc.GetObject(trans, id, OpenMode.ForRead) as BlockReference;
                if (blkRef == null)
                    return;

                var blk = UtilFunc.GetObject(trans, blkRef.BlockTableRecord, OpenMode.ForRead) as BlockTableRecord;
                if (blk == null)
                    return;

                List<AttributeDefinition> ads = new List<AttributeDefinition>();
                foreach (ObjectId entId in blk)
                {
                    var ent = UtilFunc.GetObject(trans, entId, OpenMode.ForRead);
                    if (ent is AttributeDefinition ad)
                        ads.Add(ad);
                }

                foreach (var attri in blkRef.AttributeCollection)
                {
                    AttributeReference ar = null;

                    if (attri is ObjectId)
                        ar = trans.GetObject((ObjectId)attri, OpenMode.ForWrite) as AttributeReference;
                    else if (attri is AttributeReference)
                        ar = (AttributeReference)attri;

                    var ad = ads.FirstOrDefault(a => string.Compare(a.Tag, ar.Tag, true) == 0);
                    if (ad != null)
                    {
                        var content = ar.TextString;
                        ar.SetAttributeFromBlock(ad, blkRef.BlockTransform);
                        ar.TextString = content;
                    }
                }

                trans.Commit();
            }
        }

        /// <summary>
        /// 遍历模型空间 实体外包
        /// </summary>
        public void IterateModelSpaceExtern()
        {
            StringBuilder sb = new StringBuilder();
            int count = 0;
            int errorCount = 0;
            List<Extents3d> extents3Ds = new List<Extents3d>();

            var db = HostApplicationServices.WorkingDatabase;
            using (var trans = db.TransactionManager.StartTransaction())
            {
                var blockTable = UtilFunc.GetObject(trans, db.BlockTableId, OpenMode.ForRead) as BlockTable;
                if (blockTable == null)
                    return;

                var msId = blockTable["*Model_Space"];
                UtilFunc.IterateEntitiesInBlock(msId, (id, tran) =>
                {
                    var entity = UtilFunc.GetObject(tran, id, OpenMode.ForRead) as Entity;
                    if (entity != null)
                    {
                        try
                        {
                            var ext = entity.GeometricExtents;
                            sb.AppendLine($"{count++} entity extents:<{entity.GetType().FullName}> ({ext.MaxPoint.X},{ext.MaxPoint.Y}), ({ext.MinPoint.X}, {ext.MinPoint.Y})");
                            extents3Ds.Add(ext);
                        }
                        catch (System.Exception)
                        {
                            try
                            {
                                if (entity is BlockReference br)
                                {
                                    var ext = br.GeometryExtentsBestFit();
                                    sb.AppendLine($"{count++} entity extents:<{entity.GetType().FullName}> ({ext.MaxPoint.X},{ext.MaxPoint.Y}), ({ext.MinPoint.X}, {ext.MinPoint.Y})");
                                    extents3Ds.Add(ext);
                                }
                            }
                            catch (System.Exception)
                            {
                                sb.AppendLine($"{errorCount++} invalid extents: <{entity.GetType().FullName}>");
                            }
                        }
                    }
                    return true;
                }, trans);

                UtilFunc.EnsureLayerExist(db, "ext_position", 4, true);
                foreach (var ext in extents3Ds)
                {
                    using (var line = new Line(new Point3d(ext.MinPoint.X, ext.MinPoint.Y, 0), new Point3d(ext.MaxPoint.X, ext.MaxPoint.Y, 0)))
                    {
                        UtilFunc.AddToCurrentSpace(line, trans, "ext_position");
                    }
                }


                trans.Commit();

                File.WriteAllText("d:\\content.txt", sb.ToString());
            }
        }

        /// <summary>
        /// 清理图纸中的图片资源
        /// </summary>
        public void DeleteImageInFile()
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Multiselect = true;
            if (ofd.ShowDialog() != DialogResult.OK)
                return;

            foreach (var file in ofd.FileNames)
            {
                if (File.Exists(file))
                {
                    UtilFunc.PrintMessage($"\nProcessing file: {file}");

                    var db = new Database();
                    db.ReadDwgFile(file, FileOpenMode.OpenForReadAndWriteNoShare, true, null);

                    List<ObjectId> imageIds = new List<ObjectId>();
                    using (var trans = db.TransactionManager.StartTransaction())
                    {
                        var blkTbl = UtilFunc.GetObject(trans, db.BlockTableId, OpenMode.ForRead) as BlockTable;
                        foreach (var btrId in blkTbl)
                        {
                            var btr = UtilFunc.GetObject(trans, btrId, OpenMode.ForRead) as BlockTableRecord;
                            foreach (var entId in btr)
                            {
                                var ent = UtilFunc.GetObject(trans, entId, OpenMode.ForRead) as Entity;
                                if (ent is RasterImage)
                                    imageIds.Add(ent.Id);
                            }
                        }
                    }

                    using (var trans = db.TransactionManager.StartTransaction())
                    {
                        foreach (var id in imageIds)
                        {
                            System.Exception ex = null;
                            var entity = UtilFunc.GetObject(trans, id, OpenMode.ForWrite, ex);
                            if (entity != null)
                                entity.Erase();
                            else
                                MessageBox.Show(ex.Message);
                        }
                        UtilFunc.PrintMessage($"\nFind {imageIds.Count} image! Deleting...");
                        trans.Commit();
                    }

                    using (var trans = db.TransactionManager.StartTransaction())
                    {
                        var NOD = (DBDictionary)trans.GetObject(db.NamedObjectsDictionaryId, OpenMode.ForRead);
                        var imageDict = (DBDictionary)trans.GetObject(NOD.GetAt("ACAD_IMAGE_DICT"), OpenMode.ForRead);
                        var imageDefIds = new ObjectIdCollection();
                        foreach (var entry in imageDict)
                        {
                            imageDefIds.Add(entry.Value);
                        }

                        db.Purge(imageDefIds);
                        foreach (ObjectId id in imageDefIds)
                        {
                            var imgDef = trans.GetObject(id, OpenMode.ForWrite) as RasterImageDef;
                            UtilFunc.PrintMessage($"\nDeleteing imgDef from Url: <{imgDef.LocateActivePath()}>");
                            imgDef.Erase();
                        }
                        trans.Commit();
                    }

                    var targetPath = Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "_bak.dwg";
                    db.SaveAs(targetPath, DwgVersion.Current);
                    db.Dispose();
                    UtilFunc.PrintMessage($"\nFile saved as: {targetPath}");
                }
            }
        }

        /// <summary>
        /// 合并多段线
        /// </summary>
        public void JoinCurve()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            if (doc == null)
                return;

            var p = new CuveProjection.CurveProcess();
            using (var trans = doc.TransactionManager.StartTransaction())
            {
                var ids = p.SelectPlines();
                if (ids.Any())
                {
                    p.PickPropLines(ids);
                    p.JoinCurve();
                    //p.AddLabel();
                    //p.FlatCurve();
                    //p.MoveResult(); 
                }

                trans.Commit();
            }
        }

        #endregion

        #region temp function

        /// <summary>
        /// com方式访问设置
        /// 设置默认保存格式
        /// </summary>
        private void ComPerference()
        {
            AcadPreferences preferences = (AcadPreferences)Application.Preferences;
            string supportPaths = preferences.Files.SupportPath;         // 文件搜索路径，以';'隔开的多个路径，可以读取可设置。
            string[] paths = supportPaths.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);   // 拆分成单个路径


            string printerFolder = preferences.Files.PrinterStyleSheetPath; // stb, ctb文件路径
            string[] ctbFiles = System.IO.Directory.GetFiles(printerFolder, "*.ctb");
        }

        private void SetDefaultSaveDwgFormat()
        {
            var doc = Application.DocumentManager.MdiActiveDocument;
            if (doc == null)
                return;

            Application.DocumentManager.DefaultFormatForSave = DocumentSaveFormat.R2010Dwg;
            Database db = Application.DocumentManager.MdiActiveDocument.Database;
            db.SaveAs(db.Filename, true, DwgVersion.Current, db.SecurityParameters);
            return;
        }

        private void Wcs2Ucs()
        {
            var vtr = Application.DocumentManager.MdiActiveDocument.Editor.GetCurrentView();
            if (vtr == null) return;

            var mtxWcs2Ucs = Matrix3d.AlignCoordinateSystem(Point3d.Origin, Vector3d.XAxis, Vector3d.YAxis, Vector3d.ZAxis,
                vtr.Ucs.Origin, vtr.Ucs.Xaxis, vtr.Ucs.Yaxis, vtr.Ucs.Zaxis);

            var mtxUcs2Wcs = mtxWcs2Ucs.Inverse();

            // Line line = new Line();
            // line.TransformBy(mtxUcs2Wcs);

        }

        /// <summary>
        /// 打印api的一些测试
        /// </summary>
        private void PlotSetting()
        {
            var db = HostApplicationServices.WorkingDatabase;
            using (var trans = db.TransactionManager.StartTransaction())
            {
                var layoutMgr = LayoutManager.Current;
                var curLayoutID = layoutMgr.GetLayoutId(layoutMgr.CurrentLayout);

                var currentLayout = trans.GetObject(curLayoutID, OpenMode.ForWrite) as Layout;
                PlotSettings ps = new PlotSettings(currentLayout.ModelType);
                ps.CopyFrom(currentLayout);

                PlotSettingsValidator psv = PlotSettingsValidator.Current;
                psv.SetPlotConfigurationName(ps, "Bimex.pc3", null);
                psv.SetPlotWindowArea(ps, new Extents2d(0, 0, 100, 100));
                psv.SetPlotType(ps, Autodesk.AutoCAD.DatabaseServices.PlotType.Window);
                psv.SetPlotCentered(ps, true);
                psv.SetUseStandardScale(ps, true);
                psv.SetPlotPaperUnits(ps, PlotPaperUnit.Millimeters);
                psv.SetStdScaleType(ps, StdScaleType.StdScale1To1);
                //PlotInfoValidator piv = new PlotInfoValidator();
                //PlotInfo pi = new PlotInfo();
                //pi.Layout = currentLayout.Id;
                //piv.Validate(pi);

                currentLayout.CopyFrom(ps);
                trans.Commit();
            }
        }


        #endregion
    }
}
